package network.editor;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.io.File;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;

import network.action.EditorKeyboardHandler;
import xujun.control.XContorlUtil;

import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.mxGraphOutline;
import com.mxgraph.swing.handler.mxKeyboardHandler;
import com.mxgraph.swing.handler.mxRubberband;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.util.mxUndoManager;
import com.mxgraph.util.mxUndoableEdit;
import com.mxgraph.util.mxUndoableEdit.mxUndoableChange;
import com.mxgraph.view.mxGraph;

/**
 * 拓扑编辑器面板，整个窗体的中间部分，不包括菜单和工具栏 这里有一些关于JFrame窗体的处理， 
 * 放在这里主要是为了给工具栏和菜单一个统一的处理源
 * 
 * @author zhangzuoqiang <br/>
 *         Date: Aug 25, 2011
 */
public class BasicNetworkEditor extends JPanel {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	protected mxGraphComponent graphComponent;// 图形控件，mxGraph的包装
	protected EditorTaskPanel editorTaskPanel;// 图元编辑面板
	protected mxUndoManager undoManager; // 撤销操作管理
	protected mxGraphOutline graphOutline; // 鹰眼
	protected boolean modified = false;// 是否是编辑状态，在关闭图形或窗体，图形导出时可判断是否继续执行用户的操作，如果是编辑状态，在关闭的时候需要提示用户
	protected mxRubberband rubberband;
	protected mxKeyboardHandler keyboardHandler;
	protected File currentFile;// 当前正在编辑的文件
	protected String appTitle;// 标题

	/**
	 * 添加一个undoEdit对象到undoManager管理的List<mxUndoableChange>中
	 */
	protected mxIEventListener undoHandler = new mxIEventListener() {
		public void invoke(Object source, mxEventObject evt) {
			undoManager.undoableEditHappened((mxUndoableEdit) evt
					.getProperty("edit"));
		}
	};

	/**
	 * 修改监听器，当模型或视图发生变化时，触发modified属性为true
	 */
	protected mxIEventListener changeTracker = new mxIEventListener() {
		public void invoke(Object source, mxEventObject evt) {
			setModified(true);
		}
	};

	/**
	 * 创建一个编辑面板
	 * 
	 * @param appTitle
	 *            应用程序的名称
	 * @param component
	 *            mxGraphComponent组件
	 */
	public BasicNetworkEditor(String appTitle, mxGraphComponent component) {
		this.appTitle = appTitle;
		this.graphComponent = component;
		final mxGraph graph = graphComponent.getGraph();// 获得graphComponent关联的mxGraph对象
		undoManager = new mxUndoManager();
		graph.setResetViewOnRootChange(false);// 当文件已经加载之后，不改变scale和translation
		graph.getModel().addListener(mxEvent.CHANGE, changeTracker);// 模型监听
		graph.getModel().addListener(mxEvent.UNDO, undoHandler);// undo管理器监听
		graph.getView().addListener(mxEvent.UNDO, undoHandler);// undo管理器监听
		// undo在添加到undo队列之后的处理监听
		mxIEventListener undoHandler = new mxIEventListener() {
			public void invoke(Object source, mxEventObject evt) {
				List<mxUndoableChange> changes = ((mxUndoableEdit) evt
						.getProperty("edit")).getChanges();
				graph.setSelectionCells(graph
						.getSelectionCellsForChanges(changes));
			}
		};
		/*
		 * 注意 undo的处理是分两个步骤的，首先graph的模型或视图触发了mxEvent.UNDO事件，
		 * 然后调用一个Handler，该Handler负责将mxUndoableEdit对象放置到mxUndoManager中，
		 * 然后mxUndoManager在触发mxEvent.UNDO或mxEvent.REDO事件,执行对graph的操作
		 */
		undoManager.addListener(mxEvent.UNDO, undoHandler);
		undoManager.addListener(mxEvent.REDO, undoHandler);
		graphOutline = new mxGraphOutline(graphComponent);
		editorTaskPanel = new EditorTaskPanel();
		JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
				editorTaskPanel, graphComponent);// 一左一右两块
		splitPane.setOneTouchExpandable(true);
		splitPane.setDividerLocation(300);
		splitPane.setDividerSize(4);
		splitPane.setBorder(null);

		setLayout(new BorderLayout());
		// 添加工具栏
		add(new EditorToolBar(this), BorderLayout.NORTH);
		add(splitPane, BorderLayout.CENTER);
		installHandlers();
		installListeners();
		updateTitle();
	}

	private void installHandlers() {
		rubberband = new mxRubberband(graphComponent);
		keyboardHandler = new EditorKeyboardHandler(graphComponent);
	}

	private void installListeners() {
		MouseWheelListener wheelTracker = new MouseWheelListener() {
			public void mouseWheelMoved(MouseWheelEvent e) {
				if (e.getSource() instanceof mxGraphOutline
						|| e.isControlDown()) {
					BasicNetworkEditor.this.mouseWheelMoved(e);
				}
			}

		};
		graphOutline.addMouseWheelListener(wheelTracker);
		graphComponent.addMouseWheelListener(wheelTracker);
		graphOutline.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				mouseReleased(e);
			}

			public void mouseReleased(MouseEvent e) {
				if (e.isPopupTrigger()) {

				}
			}

		});

		graphComponent.getGraphControl().addMouseListener(new MouseAdapter() {

			public void mousePressed(MouseEvent e) {
				mouseReleased(e);
			}

			public void mouseReleased(MouseEvent e) {
				if (e.isPopupTrigger()) {
					// showGraphPopupMenu(e);
				}
			}

		});

		graphComponent.getGraphControl().addMouseMotionListener(
				new MouseMotionListener() {
					public void mouseDragged(MouseEvent e) {
					}

					public void mouseMoved(MouseEvent e) {
						mouseDragged(e);
					}
				});
	}

	public void updateTitle() {
		JFrame frame = (JFrame) SwingUtilities.windowForComponent(this);

		if (frame != null) {
			String title = (currentFile != null) ? currentFile
					.getAbsolutePath() : "新文档";

			if (modified) {
				title += "*";// 如果是编辑状态，标题上加个*号提示一下
			}

			frame.setTitle(title + " - " + appTitle);
		}
	}

	public void setModified(boolean modified) {
		boolean oldValue = this.modified;
		this.modified = modified;

		firePropertyChange("modified", oldValue, modified);

		if (oldValue != modified) {
			updateTitle();
		}
	}

	public boolean isModified() {
		return modified;
	}

	// 退出窗体
	public void exit() {
		JFrame frame = (JFrame) SwingUtilities.windowForComponent(this);

		if (frame != null) {
			frame.dispose();
		}
	}

	public Action bind(String name, final Action action) {
		return bind(name, action, null);
	}

	public Action bind(String name, final Action action, String iconUrl) {
		return new AbstractAction(name,
				(iconUrl != null) ? XContorlUtil.getImageIcon(iconUrl) : null) {
			
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) {
				action.actionPerformed(new ActionEvent(getGraphComponent(), e
						.getID(), e.getActionCommand()));
			}
		};
	}

	public mxGraphComponent getGraphComponent() {
		return graphComponent;
	}

	public mxUndoManager getUndoManager() {
		return undoManager;
	}

	public void setCurrentFile(File file) {
		File oldValue = currentFile;
		currentFile = file;

		firePropertyChange("currentFile", oldValue, file);

		if (oldValue != file) {
			updateTitle();
		}
	}

	public mxGraphOutline getGraphOutline() {
		return graphOutline;
	}

	public File getCurrentFile() {
		return currentFile;
	}

	protected void mouseWheelMoved(MouseWheelEvent e) {
		if (e.getWheelRotation() < 0) {
			graphComponent.zoomIn();
		} else {
			graphComponent.zoomOut();
		}
	}

	public JFrame createFrame(JMenuBar menuBar) {
		JFrame frame = new JFrame();
		frame.getContentPane().add(this);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setJMenuBar(menuBar);
		frame.setSize(1024, 768);
		updateTitle();
		return frame;
	}
}